跳转到内容

可视化编辑器

这些源表可以用 Elin ModMaker 在浏览器里编辑。同一个角色的贴图、立绘、台词、剧情与各语言译文会聚合到一处,种族、职业、元素这类跨表引用带补全与校验。

剧情 ​

剧情是由多选项对话和附加动作组成的交互系统。

添加剧情 ​

要给角色添加默认剧情文件,把 id.xlsx 放到 LangMod/**/Dialog/Drama/ 文件夹下,以角色 ID 作为文件名(例如 tinymita 角色对应 tinymita.xlsx)。游戏会自动加载与角色同 ID 的剧情文件。

如果剧情文件用了别的名字,例如 DramaFileId.xlsx,需要在角色源表的 tag 列额外添加 addDrama(DramaFileId) 标签。

你也可以使用 C# API chara.SetDramaOverride(DramaFileId) 或 chara.ShowDialog(DramaId, step)。

制作新剧情表时可参考游戏内置表 Elin/Package/_Lang_Chinese/Lang/CN/Dialog/Drama,或下载 Tiny Mita 示例作为模板。

热重载

剧情表可以在游戏运行时编辑,改动会在下次打开对话时自动生效。

定义 ​

行 ​

剧情表由多行组成,按从上到下的顺序读取。每行包含以下字段(由首行定义):

  • step:标记步骤起点;后续行均属于该步骤,直到出现下一个 step。
  • jump:本行执行后跳转的目标步骤。
  • if / if2:执行前需检查的条件。若存在 if2,则两者必须同时满足。
  • action:要执行的动作。
  • param:动作参数。
  • actor:动作或对话行的说话者。
    • ?:显示为 ???。
    • tg:剧情目标角色。actor 为空时默认使用。
    • narrator:默认旁白。
    • pc:玩家。
  • id:唯一标识,text 和 choice 行必须填写。其他行无需填写。
  • text_XX / text_JP / text_EN / text:对话内容。XX 为语言代码(例如 text_CN、text_ZHTW)。缺少对应的非内置语言时,改用 text。text_JP 和 text_EN 必须填写,但不一定要翻译。

(点击放大) img

步骤 ​

剧情流程由多个步骤组成。每个步骤可包含一行或多行对话、动作与条件。

main 是默认起始步骤,end 用于退出剧情。

创建表时请避免使用以 _ 或 flag 开头的步骤名,以免与内部步骤冲突。

内置步骤 ​

内置步骤

剧情加载时总会注入以下内置步骤,把它们设为 jump 目标即可使用。inject/Unique 动作还会插入引用这些步骤的默认对话选项(“聊聊天”等),所以通常不必自己重复添加。

步骤名用途
_banish结束剧情
_bye结束剧情
_toggleSharedEquip切换 tg 的共享装备状态
_daMakeMaid将 tg 设为女仆
_joinParty若 tg 的特质允许加入,则将其设为队伍成员。这不是邀请!
_leaveParty将 tg 从队伍移除,并送回据点区域
_makeLivestock将 tg 设为派系家畜
_makeResident将 tg 设为派系居民
_depart将 tg 从派系移除
_rumor查看流言
_sleepBeside切换 tg 是否在玩家旁睡觉
_disableLoyal切换 tg 忠诚心状态
_sucktg 吸血或吸猫。优先吸血,其次吸猫
_insult切换 tg 嘲讽状态
_makeHome将当前区域分支设为 tg 的家
_invite尝试邀请 tg 成为同伴,会检查玩家属性和 tg 是否可邀请。要无条件邀请入队,请使用拓展动作 join_party()
_Guide引导玩家前往一系列地点
_tail纯洁的肉体关系
_whore有金钱交易的肉体关系
_bloom加深与 tg 的羁绊
_buy从 tg 购买物品
_buyPlan从 tg 购买研究图纸
_give给 tg 物品
_blessing对队伍施加祝福
_train跟 tg 训练技能
_changeDomain改变 tg 的领域
_revive复活死亡的同伴
_buySlave从 tg 购买奴隶
_trade与 tg 交换物品
_identify让 tg 鉴定物品
_identifyAll让 tg 鉴定所有物品
_identifySP让 tg 用高级技能鉴定物品
_bout发起决斗
_news在地图上生成随机地城
_heal治疗玩家
_food从 tg 购买食物
_deposit向 tg 存款
_withdraw向 tg 取款
_copyItem让 tg 复制物品
_extraTax缴纳额外税金
_upgradeHearth升级炉石
_sellFame出售声望
_investZone投资当前区域
_investShop投资 tg 的商店
_changeTitle更改玩家称号
_buyLand扩展当前区域地图
_disableMove使 tg 无法移动
_enableMove使 tg 可以移动

文本 ​

text_JP、text_EN、text_XX 列中的文本会作为对话事件,玩家必须点击或按键才能继续。除非动作另有说明,动作行不能和文本行合并。

随机话题 ​

$topic 会从 chara_talk.xlsx 定义的话题中随机选取一行。这个文件可以是 Elin 默认的 Package/_Lang_Chinese/Lang/CN/Data/chara_talk.xlsx,也可以是 LangMod/**/Data/chara_talk.xlsx。例如,$sup 会随机选用以下行之一:

什么?
什么东西?
哦?
咦?
喂!
哦哦。
咦。
嗯?

替换 ​

文本替换值
#tg_histg 的所有格代词
#tg_himtg 的宾格代词
#tgtg 角色名
#last_choice上一次选项的文本
#newline换行符
#costHire雇佣 tg 的花费(数值本地化)
#self / #me角色 tg 的全名(含头衔)
#histg 的所有格代词
#himtg 的宾格代词
#1 ~ #5外部变量 refDrama1~refDrama5(数字自动格式化)
#god神名称(若 tg 不为空用其信仰,否则随机宗教)
#player / #title玩家头衔
#zone当前区域名
#guild_title当前剧情关联公会的头衔
#guild当前剧情公会名
#race玩家种族名
#pc玩家简名
#pc_full玩家全名(含头衔)
#pc_his玩家的所有格代词
#pc_him玩家的宾格代词
#pc_race玩家种族
#aka玩家别名
#bigdaddy本地化字符串 "bigdaddy"
#festival节日名(若区域有节日则用对应节日名,否则通用)
#brother2“兄弟”或“姐妹”(根据玩家性别)
#brother兄弟/姐妹随机称呼(bro 或 sis 列表随机)
#onii2哥哥/姐姐随机称呼(列表 onii2/onee2)
#onii哥哥/姐姐随机称呼(列表 onii/onee)
#gender玩家性别对应的称呼(按性别索引 gendersDrama 列表)
#he“他”或“她”(根据玩家性别)
#He同上,首字母大写

这些替换在 dialog.xlsx 里也能用。

动态内容 ​

文本列以 #eval <此处为 C# 脚本..> 开头并返回 string 时,可以动态生成文本内容。

动作 ​

文本行最常见,只包含 id、text 列(可选 if 条件),执行时需要玩家输入(点击或按键)才能继续。

动作行(choice 除外)自动执行,无需输入。若同一行同时存在 action 和 text,通常会忽略 text。

例如,紧跟在文本行后面的动作行,要等玩家点过文本行才会执行。

内置动作 ​

内置动作
动作参数说明
injectUnique插入“来聊聊吧!”及大量实用步骤
choice为上一个文本行添加选项。需配合 text 和 jump 使用
choice/bye插入默认告别选项
cancel设置右键/ESC 键行为。需配合 jump,通常设为 end
setFlagflag 名称,值(可选)设置 flag 值,未提供值时默认为 1
reload重新加载剧情,让当前剧情里改过的 flag 生效。jump 可选,通常设为 main;省略时在原地继续执行。这与热重载无关,开发时热重载只需保存文件并重新打开对话
enableTone为整个剧情启用对话语气转换
addActor添加剧情角色供后续使用,text 可以用来覆盖名称。在 actor 单元格填写新 ID 时会自动触发。actor 需填写角色 ID
invoke方法名调用方法。可用方法均为 Elin 本体专用。
setBG图片名(可选)设置背景图,留空则清除。支持在 Texture 文件夹提供自定义 png
BGMBGM ID切换至指定 BGM。自定义 BGM 详见 音频/BGM 页面
stopBGM停止 BGM 且不继续
lastBGM停止 BGM 并继续播放上一个
sound音频 ID播放指定音频。自定义音频详见 音频/BGM 页面
wait时长暂停执行指定秒数,适合等待动画完成
alphaIn alphaOut时长透明度过渡(秒)
alphaInOut时长,等待时间alphaIn 先执行,等待指定秒数,再执行 alphaOut
fadeIn fadeOut时长,white/black(可选)淡入淡出过渡(秒)
fadeInOut时长,等待时间,white/black(可选)先 fadeIn,等待指定秒数,再 fadeOut
hideUI过渡时间以过渡效果隐藏 HUD 元素,退出剧情时恢复
hideDialog隐藏剧情对话框以便制作过场动画,但文本行会强制显示对话框,需配合 wait 使用
end直接结束剧情,等同于 jump 至 end 步骤
addKeyItem关键物品 ID给予玩家关键物品
drop物品 ID在玩家位置掉落奖励物品
addResource资源名称,数量添加家园资源
shake屏幕震动
slap剧情所有者角色扇玩家(在对话关闭后执行,玩家受到少量伤害)
destroyItem物品 ID从玩家背包中查找并销毁指定物品
focus立即将镜头聚焦到剧情所有者角色
focusChara角色 ID,速度(可选)将镜头移动并聚焦到同地图角色
focusPC速度(可选)将镜头聚焦到玩家
unfocus重置并取消聚焦镜头
destroy角色 ID移除同地图角色
save存档
setHour小时数设置游戏时间

提供多个参数时,使用半角逗号(,)分隔,中间不要有空格。

条件 ​

条件用于判断该行是否执行。

静态条件 ​

静态条件在加载时仅判定一次。

使用 if(可选 if2)列为任意行附加条件。

条件参数说明
hasFlagflag 名称玩家拥有该 flag 且值不为 0
!hasFlagflag 名称玩家没有该 flag 或值为 0
hasMelilithCurse玩家拥有 Melilith 诅咒
merchant玩家在商人公会
fighter玩家在战士公会
thief玩家在盗贼公会
mage玩家在法师公会
hasItem物品 ID玩家背包中拥有该物品
isCompleted任务 ID玩家已完成指定任务

简单值检查(最常用于 flag/计数器):

=,example_flag,1
>,example_counter,20
!,example_flag,69

大多数行只需使用 if 列。若需多条件判断,可添加 if2 列。

动态条件 ​

要动态启用或禁用某行,可以使用:

  • invoke* 条件表达式,或
  • 返回 bool 的 eval 动作

分支 ​

把 jump 设为某个剧情步骤,即可跳转到该步骤。

将 jump 设置为 eval_result,并使用返回 string 的 eval 动作,可动态选择跳转目标。

invoke* 表达式 ​

在剧情表中,可使用特殊动作 invoke*(或简写 i*)调用拓展方法:

语法 ​

这套伪代码的语法很简单:把 action 设为 invoke* 或 i*,param 填一个有效方法:

动作参数actor
invoke*/i*honk_honk(arg1, arg2)pc

这会调用名为 honk_honk 的方法,并传入 arg1、arg2 两个参数。

条件组合可使用简写前缀:!expr = not(expr)、&expr = and(expr)、?expr = or(expr)。

参数 ​

参数写在拓展方法的括号内,用半角逗号 , 分隔,和代码写法类似。没有参数时写空括号 ()。

大多数方法会以 actor 单元格指定的角色为目标,例如 pc、tg(剧情目标角色)或任何有效的角色 ID。默认值为 tg。

若同一行的 jump 单元格有值,拓展方法的返回值决定是否执行该 jump:返回 true 时跳转,否则不跳转。

数值表达式语法 ​

DramaValueExpression 用于判定或赋值。

示例:

表达式含义
69赋值为 69
=69赋值为 69
+5在原值基础上增加 5
-3在原值基础上减少 3
*10在原值基础上乘以 10
/2在原值基础上除以 2
==69判断是否等于 69
!=114判断是否不等于 114
>10判断是否大于 10
>=20判断是否大于等于 20
<5判断是否小于 5
<=3判断是否小于等于 3
++在原值基础上增加 1
--在原值基础上减少 1
x10在原值基础上乘以 10(* 的别名)

拓展动作 ​

方法参数说明跳转条件
add_item物品 ID, 材质 alias(可选), 等级(可选), 数量(可选)为 actor 添加指定物品,默认使用物品自身材质、自动等级、数量 1总是
equip_item物品 ID创建指定物品并装备到 actor总是
destroy_item物品 ID, 数量(可选)从 actor 销毁指定物品,默认数量 -1,即销毁全部同 ID 物品总是
join_faith信仰 ID(可选)使 actor 加入指定信仰,留空则退出当前信仰总是
join_party无条件使 actor 加入玩家队伍总是
apply_condition状态 alias, 强度为 actor 施加状态,默认强度 100总是
remove_condition状态 alias移除 actor 的状态总是

拓展演出 ​

方法参数说明跳转条件
move_next_to角色 ID使 actor 移动到同地图角色身旁找到目标时
move_tileX 偏移, Y 偏移让 actor 按相对坐标移动,例如 1,1 或 2,-1总是
move_toX, Y让 actor 按绝对坐标移动,例如 64,44 或 12,0总是
move_zone区域 ID, 层数(可选)传送 actor 到指定区域,默认 0 层成功时
move_zone_2区域全名使用区域全名语法传送 actor,例如 derphy@-1成功时
play_anime动画 ID使 actor 执行动画总是
play_effect特效 ID使 actor 播放特效总是
play_effect_at特效 ID, X, Y在地图指定位置播放特效总是
play_emote表情 ID, 时长(可选)使 actor 显示表情,默认时长 1 秒总是
play_screen_effect屏幕特效 ID播放屏幕特效总是
pop_text文本使 actor 发出喊叫文本(气泡框)总是
set_portrait头像 ID(可选)设置 actor 对话时使用的头像,留空则重置。支持 Portrait 文件夹中的自定义头像,例如 UN_myChara_happy.png 可以写作 happy 或 UN_myChara_happy找到 actor 时
set_portrait_override头像 ID(可选)设置 actor 对话外使用的头像,留空则重置。支持 Portrait 文件夹中的自定义头像,必须使用全名。不会影响当前对话的头像总是
set_sprite贴图 ID(可选)设置 actor 的自定义贴图,留空则重置。从 Texture 文件夹读取总是
show_book分类/书籍 ID打开一本书,支持 LangMod/**/Text 文件夹,例如使用 Book/ok 对应 Text/Book/ok.txt成功时

拓展修改 ​

方法参数说明跳转条件
mod_affinity数值表达式使用数值表达式调整 actor 好感度总是
mod_currency货币种类, 数值表达式使用数值表达式修改 actor 的货币。money money2 plat medal influence casino_coin ecopo总是
mod_element元素 alias, 强度(可选), 潜能(可选)为 actor 修改指定元素(特质/抗性/技能等),默认强度 1,潜能 100。不同类型元素的强度缩放规则不同总是
mod_element_exp元素 alias, 数值表达式修改 actor 指定元素的经验值总是
mod_fame数值表达式使用数值表达式修改玩家声望总是
mod_flagflag, 数值表达式(可选)使用数值表达式修改 actor 的 flag 值,例如 +1、=1、0,默认 =1。支持非玩家角色。注意:此类逐角色 flag 与 setFlag/hasFlag 的对话 flag 是两套独立存储总是
mod_keyitem关键物品 alias, 数值表达式(可选)使用表达式修改玩家关键物品值,默认 =1成功时

拓展条件 ​

这些同样是通过 invoke* 动作调用的拓展方法,只是返回值有特殊含义。

方法参数说明跳转条件
if_affinity数值表达式使用表达式检查 actor 好感度,例如 <5、>=90、!=0满足时
if_condition状态 alias, 数值表达式(可选)检查 actor 是否拥有指定状态,可用表达式检查其强度,默认 >=1拥有时
if_currency货币种类, 数值表达式使用表达式检查 actor 的货币。money money2 plat medal influence casino_coin ecopo满足时
if_element元素 alias, 数值表达式使用表达式检查 actor 的元素满足时
if_faith信仰 ID, 奉献等级(可选)检查 actor 是否加入特定信仰且满足奉献等级,默认 >=0满足时
if_fame数值表达式使用表达式检查玩家声望满足时
if_flagflag 名称, 数值表达式(可选)使用表达式检查 actor 的 flag 值,例如 =5、1、!=0,默认 >=1。读取的是与 mod_flag 相同的逐角色 flag,而非 setFlag/hasFlag 的对话 flag满足时
if_lv数值表达式使用表达式检查 actor 的等级满足时
if_has_item物品 ID, 数值表达式(可选)检查 actor 是否拥有符合表达式的物品数量,默认 >=1满足时
if_hostility阵营数值表达式检查 actor 是否符合特定阵营,例如 =Ally 或 >Enemy。阵营值从小到大依次为 Enemy、Neutral、Friend、Ally满足时
if_in_partytrue 或 false(可选)检查 actor 是否在玩家队伍中,默认 true满足时
if_keyitem关键物品 alias, 数值表达式(可选)检查玩家是否拥有符合表达式的关键物品,默认 >0满足时
if_race种族 ID检查 actor 是否为对应种族满足时
if_tag标签检查 actor 是否拥有 Chara 行中定义的标签满足时
if_zone区域 ID, 层数(可选)检查 actor 是否处于指定区域(可选检查层数)满足时
if_zone_2区域全名使用区域全名语法检查 actor 是否处于指定区域,例如 derphy@-1满足时

特殊方法 ​

方法参数说明跳转条件
console_cmd控制台命令 参数1 参数2...执行控制台命令总是
evalC# 脚本或使用 <<<path.cs 语法指定文件执行 C# 脚本。推荐使用 eval 动作(而非 invoke* 调用 eval())返回 true 时
andand(if_flag(flag1, >0), if_flag(flag2, <0)...)将 invoke* 表达式作为参数,评估全部条件全部满足时
oror(if_race(lich), if_race(snail)...)将 invoke* 表达式作为参数,评估全部条件任意满足时
notnot(if_zone(dungeon), if_zone(field), if_zone(underground)...)将 invoke* 表达式作为参数,评估全部条件全部不满足时

API ​

Elin 提供了一套简单的 API,可以从你自己的脚本 DLL 添加自定义拓展方法。

动作解析器 ​

解析器在加载剧情时调用,负责处理已注册的非内置动作行并创建事件。

cs
[ElinDramaActionParser("my_action")]
public static bool ExampleParser(DramaManager dm, Dictionary<string, string> line) 
{
    dm.AddEvent(new DramaEventTalk(line["actor"], () => {
        // 处理逻辑
        return "一行文本!";
    }));
    // 该行已处理
    return true;
}
// 或手动注册
CustomDramaExpansion.AddDramaActionParser("my_action", ExampleParser);

动作调用 ​

调用方法会自动编译成 invoke* 表达式,剧情执行到该行时调用。

cs
[ElinDramaActionInvoke]
public static bool add_item(DramaManager dm, Dictionary<string, string> line,
                            string itemId,
                            string materialAlias = "wood",
                            int lv = -1,
                            int count = 1)
{
    var chara = dm.GetChara(line["actor"]);

    var mat = sources.materials.alias.TryGetValue(materialAlias, "wood");
    var item = ThingGen.Create(itemId, mat.id, lv).SetNum(count);
    chara.Pick(item);

    return true;
}

[ElinDramaActionInvoke("nodiscard")]
public static bool if_element(DramaManager dm, Dictionary<string, string> line,
                              string elementAlias,
                              DramaValueExpression expr) 
{
    var chara = dm.GetChara(line["actor"]);
    return chara.HasElement(elementAlias) && expr.Compare(chara.Evalue(elementAlias));
}
cs
[ElinDramaActionInvoke]
public static bool console_cmd(DramaManager dm, Dictionary<string, string> line, 
                               params string[] parameters)
{
    string.Join(' ', parameters).EvaluateAsCommand();
    return true;
}

剧情调用方法必须为 static,返回 bool,参数以 DramaManager dm, Dictionary<string, string> line 开头。

实际表达式参数可由 Elin 自动转换,或以 string[] 形式传入。可自动转换的参数包括内置数据类型、DramaValueExpression 或具有 static bool TryParse(string, out T) 方法的自定义类型。

更多示例请参阅 CustomDramaExpansion 的实现。

文字样式 ​

可以用下面这些标签给文字加上粗体、斜体、颜色等样式:

标签说明用法示例
<b> </b>粗体<b>粗体文本</b>
<i> </i>斜体<i>斜体文本</i>
<size=...> </size>字号(像素)<size=22>大</size>
<color=...> </color>文字颜色(英文名称/#hex)<color=red>红</color> <color=#add8e6ff>亮蓝</color>

在 drama 表里用 Alt + Enter 换行,文字会在同一页内分行显示,这一点与 dialog.xlsx 不同。

也可以用 #newline 换行。

完整说明见 Unity 富文本标签文档

脚本 ​

你可以在剧情表中使用 eval 动作直接运行 C# 代码。

脚本能力与常规 C# 相同,区别在于:

  • 脚本状态与当前剧情实例绑定(会持续到剧情结束,然后自动重置)。
  • 快捷方式:dm = DramaManager,line = 当前行(Dictionary<string, string>),tg = 目标 Chara,pc = 玩家 Chara。

返回值行为:

  • 返回 bool + 有效的 jump 目标 → 决定是否执行跳转。
  • 返回 string + jump 单元格设置为 eval_result → 使用该字符串作为新的跳转目标。
  • 没有返回值 → 按普通动作执行。

从同一文件夹导入脚本文件的写法:<<<script_snippet.cs

传递变量 ​

使用共享的 Script 字典:

cs
// 第一次 eval
var value = EClass.rnd(100) * 5;
Script["random_value"] = value;

// 后续 eval
var value = (int)Script["random_value"];

常用示例 ​

功能代码
跳转到指定步骤dm.Goto("my_new_step");
添加“来聊聊吧!”选项dm.InjectUniqueRumor();
添加临时对话dm.AddTempTalk("text", "actor", "jump");
获取 Chara 实例var chara = dm.GetChara("tg");
招募到队伍chara.MakeAlly();
修改等级chara.SetLv(chara.LV + 5);

需要帮助可以在 Elona Discord 上联系 @freshcloth,或发邮件。

This project is an unofficial documentation site and is not affiliated with, endorsed by, or associated with Elin or Lafrontier / Noa. All trademarks are the property of their respective owners.